#pragma once

#include "PopupWnd.h"
#include "ListItem.h"
#include "Timer.h"

#include <vector>
#include "BufferedWnd.h"

class CListStyle
{
public:
  COLORREF clrBackground1;
  COLORREF clrBackground2;
  HBRUSH   hbrBackground;

  CListStyle() : clrBackground1(0), clrBackground2(0), hbrBackground(NULL)
  {}
};

class CList : public CBufferedWnd
{
protected:
  typedef std::vector<CListItem*> CListItems;

public:
  CList(int itemHeight = 0);
  virtual ~CList();

// Message map and handlers
	BEGIN_MSG_MAP(CList)
    MSG_WM_CREATE(OnCreate)
    MSG_WM_DESTROY(OnDestroy)
    MSG_WM_SIZE(OnSize)

    MSG_WM_KEYDOWN(OnKeyDown)
    MSG_WM_KEYUP(OnKeyUp)

    MSG_WM_LBUTTONDOWN(OnLButtonDown)
    MSG_WM_MOUSEMOVE(OnMouseMove)
    MSG_WM_LBUTTONUP(OnLButtonUp)

    CHAIN_MSG_MAP(CBufferedWnd)
	END_MSG_MAP()

  int OnCreate(LPCREATESTRUCT lpCreateStruct)
  {
    GetClientRect(&rcClient);
    UpdateScrollMax();
    SetMsgHandled(FALSE);
    return 0;
  }

  void OnDestroy()
  {
    Clear();
    SetMsgHandled(FALSE);
  }

  virtual void OnSize(UINT nType, CSize size)
  {
    GetClientRect(&rcClient);
    UpdateScrollMax();
    SetMsgHandled(FALSE);
  }

  virtual void Clear();

  virtual void Draw(CDCHandle dc, const CRect& rClip);
  void DrawBackground(CDCHandle& dc, const CRect& rClip);
  void DrawForeground(CDCHandle& dc, const CRect& rClip);

  virtual void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
  virtual void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);

  virtual void OnLButtonDown(UINT nFlags, CPoint point);
  virtual void OnMouseMove(UINT nFlags, CPoint point);
  virtual void OnLButtonUp(UINT nFlags, CPoint point);

  virtual void OnLongTap();

  virtual void Redraw()
  { InvalidateRect(NULL, FALSE); }
  virtual void RedrawItem(int index)
  { if (index >= 0 && index < (int)items.size())
      InvalidateRect(&GetItemRect(index), FALSE); }

  virtual const CRect& GetRect() const
  { return rcClient; }

  int GetHeight() const
  { return height; }

  void SetStyle(const CListStyle& listStyle)
  { style = listStyle; }
  const CListStyle& GetStyle() const
  { return style; }

  void SetScroll(int nScrolled)
  { scrolled = min(scrollMax, max(nScrolled, 0)); }
  int  GetScroll() const
  { return scrolled; }

  bool IsDragging() const
  { return isDragging; }
  bool IsScrolling() const
  { return isScrolling; }
  bool IsBouncing() const
  { return scrollOffset != 0; }
  bool IsMoving() const
  { return isDragging || isScrolling || scrollOffset != 0; }

  int GetCount() const
  { return (int)this->items.size(); }

  virtual void Add(CListItem* pItem);

  virtual CListItem* GetAt(int index)
  { return this->items[index]; }
  virtual const CListItem* GetAt(int index) const
  { return this->items[index]; }

  virtual void CalculateHeights();

  int GetSelection(void) const
  { return this->selection; }
  void SetSelection(int index = -1, bool redraw = false);
  int GetHover(void) const
  { return this->hover; }
  void SetHover(int index = -1, bool redraw = true);

  CListItem* GetSelectedItem()
  {
    if (this->selection >= 0 && this->selection < (int)items.size())
      return this->items[this->selection];
    else
      return NULL;
  }
  const CListItem* GetSelectedItem() const
  { return GetSelectedItem(); }

public:
  void OnLongTapTimer(UINT nID, DWORD dwTime);
  void OnLongTapIndTimer(UINT nID, DWORD dwTime);
  void OnBounceTimer(UINT nID, DWORD dwTime);
  void OnScrollTimer(UINT nID, DWORD dwTime);
  void OnScrollToTimer(UINT nID, DWORD dwTime);

  bool IsItemVisible(int index) const;

  void SelectPrev();
  void SelectNext();
  void ScrollToSelection(bool smoothScroll = true);

  virtual void ScrollTo(int pos, int time);
  virtual void StopScrolling();

  int  GetItemIdx(int y) const;
  int  GetItemIdx(const CListItem* item) const;
  RECT GetItemRect(int idx, bool screen = true) const;
  RECT GetItemClientRect(int idx) const;

  int  GetDefaultItemHeight() const
  { return defaultItemHeight; }
  int  GetAvgItemHeight() const
  { return avgItemHeight; }

protected:
  void UpdateScrollMax()
  { scrollMax = height - (rcClient.bottom - rcClient.top); 
    if (scrollMax < 0) scrollMax = 0; }

  void MoveList(double delta);

  void StartLongTap();
  void DrawLongTapInd(HDC hdc, int item, int progress);
  
  void DrawScrollBar(HDC hdc, const RECT& rClip);

protected:
  static CScreenDC s_dcTmp;

  int  startY;
  int  lastY;
  bool isMinimizing;

  bool isScrolling;
  bool isDragging;
  int  scrolled;
  int  scrollMax;
  double scrollSpeed;
  double scrollLastSpeed;
  double scrollOffset;
  double scrollStart;
  double scrollChange;
  double scrollDuration;
  DWORD  scrollStartTime;
  DWORD  tStartTime;
  CTimerInst<CList, &CList::OnScrollTimer>   scrollTimer;
  CTimerInst<CList, &CList::OnScrollToTimer> scrollToTimer;
  CTimerInst<CList, &CList::OnBounceTimer>   bounceTimer;

  CRect rcClient;
  int  height;
  int  defaultItemHeight;

  std::vector<int> itemPos;
  int              avgItemHeight;

  CListItems items;
  CListStyle style;

  int  selection;
  int  hover;

  CTimerInst<CList, &CList::OnLongTapTimer>    longTapTimer;
  CTimerInst<CList, &CList::OnLongTapIndTimer> longTapIndTimer;
  DWORD tLongTapStartTime;
  bool skipMouseAction;
  bool skipKeyboardAction;
};